package com.scs.application.modules.ext.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.consts.ErrorCodes;
import com.scs.application.core.exceptions.BusinessException;
import com.scs.application.core.model.RestResult;
import com.scs.application.core.mvc.controller.CrudController;
import com.scs.application.core.utils.FileUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.modules.doc.entity.FileInfo;
import com.scs.application.modules.doc.service.FileInfoService;
import com.scs.application.modules.ext.entity.File;
import com.scs.application.modules.ext.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统文件上传表 前端控制器
 * </p>
 */
@RestController
@RequestMapping("/sys/file")
@Slf4j
public class SysFileController extends CrudController<File, String, FileService> {
    @Value("${res.real-path}")
    private String resPath;

    @Autowired
    private FileInfoService fileInfoService;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /***
     * @Description：根据 关联信息查询文件列表
     */
    @PostMapping("/getFilesByRef")
    public RestResult getFilesByRef(@RequestBody Map<String, String> reqData) {
        String fileRefTable = reqData.get("fileRefTable");
        String fileRefId = reqData.get("fileRefId");
        String fileInfoIdArrStr = reqData.get("fileInfoIdArr");
        String fileInfoField = reqData.get("fileInfoField");
        if (StringUtils.isBlank(fileRefTable)) return RestResult.error("附件查询失败,缺少参数【refTable】");
        if (StringUtils.isBlank(fileRefId)) return RestResult.error("附件查询失败,缺少参数【refId】");

        // 迁移file_info表的数据
        if (StringUtils.isNotBlank(fileInfoIdArrStr)) {
            String[] fileInfoIdArr = fileInfoIdArrStr.split(",");
            Map<String, File> fileByRefInfoId = service.list(Wrappers.<File>query().in("ref_info_id",fileInfoIdArr)).stream().collect(Collectors.toMap(File::getRefInfoId, item -> item));
            if (fileByRefInfoId == null) {
                fileByRefInfoId = new HashMap<>();
            }
            if (fileInfoIdArr.length != fileByRefInfoId.keySet().size()) {
                List<FileInfo> fileInfoList = fileInfoService.list(Wrappers.<FileInfo>query().in("id", fileInfoIdArr).orderByDesc("gmt_create"));
                if (!fileInfoList.isEmpty()) {
                    for(FileInfo fileInfo : fileInfoList) {
                        File fileSave = new File();
                        fileSave.setRefId(fileRefId);
                        fileSave.setRefInfoId(fileInfo.getId());
                        fileSave.setRefTable(fileRefTable);
                        fileSave.setFileName(fileInfo.getName());
                        fileSave.setFilePath("/res/" + fileInfo.getPath().replace(resPath, ""));
                        service.save(fileSave);
                    }
                }
                if (StringUtils.isNotBlank(fileInfoField)) {
                    jdbcTemplate.execute(StrUtil.format("update {} set {} = null where id = '{}'", fileRefTable,fileInfoField, fileRefId));
                }
            }
        }

        List<File> files = service.list(Wrappers.<File>query().eq("ref_table", fileRefTable).eq("ref_id", fileRefId).orderByDesc("gmt_create")
        );
        return RestResult.ok("附件查询成功", files);
    }

    /***
     * @Description：根据 删除文件
     */
    @GetMapping("/delFilesByRef")
    public RestResult delFilesByRef(String fileId) {
        if (StringUtils.isBlank(fileId)) return RestResult.error("附件删除失败,缺少参数【fileId】");

        File fileDel = service.getOne(Wrappers.<File>query().eq("id", fileId));
        if (fileDel != null) {
            service.removeById(fileDel.getId());
            FileUtils.deleteFile(resPath + fileDel.getFilePath().replaceFirst("/res/", ""));
        }
        return RestResult.ok("附件删除成功");
    }

    /**
     * 多文件上传
     *
     * @Description：多文件上传 【需要统一存放】
     */
    @RequestMapping(value = "/uploadMore", method = RequestMethod.POST)
    public RestResult uploadMore(@RequestParam Map<String, String> reqData, @RequestParam("file") MultipartFile file, HttpServletRequest request) {
        RestResult result = RestResult.ok("文件上传成功");
        //文件保存路径
        String fileSavePath = reqData.get("fileSavePath"), fileAccessPath;
        String refTable = reqData.get("fileRefTable");
        String refId = reqData.get("fileRefId");
        if (StringUtils.isBlank(fileSavePath)) fileSavePath = "tmp";
        fileSavePath += "/";
        fileAccessPath = fileSavePath;
        fileSavePath = resPath + fileSavePath;

        BufferedOutputStream stream = null;
        String fileName = IdUtil.getSnowflakeNextIdStr() + "." + FileUtils.getFileExtension(file.getOriginalFilename());
        try {
            FileUtils.mkdirs(fileSavePath);
            if (StringUtils.isBlank(refTable)) throw new BusinessException("缺少参数【refTable】");
            if (StringUtils.isBlank(refId)) throw new BusinessException("缺少参数【refId】");

            byte[] bytes = file.getBytes();
            stream = new BufferedOutputStream(new FileOutputStream(
                    new java.io.File(fileSavePath + fileName)));//设置文件路径及名字
            stream.write(bytes);// 写入
            stream.close();

            File fileSave = new File();
            fileSave.setRefId(refId);
            fileSave.setRefTable(refTable);
//            fileSave.setFileName(fileName);
            fileSave.setFileName(file.getOriginalFilename());
            fileSave.setFilePath("/res/" + fileAccessPath + fileName);
            service.save(fileSave);
            result.setData(fileSave);
        } catch (Exception e) {
            result.setMsg("文件上传失败," + e.getMessage());
            result.setCode(ErrorCodes.COMMON_ERROR);
        }
        return result;
    }

    /**
     * 下载文件 根据绝对路径
     * @param response
     */
    @RequestMapping("downloadById")
    @ResponseBody
    public String downloadById(HttpServletResponse response, String fileId) {
        File fileInfo = service.getById(fileId);
        if (fileInfo == null) {
            log.error("下载失败，文件表未找到,文件id {}",fileId);
            return "下载失败，文件表未找到";
        }
        OutputStream os = null;

        response.setHeader("content-type", "application/octet-stream");
        response.setContentType("application/octet-stream");
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + java.net.URLEncoder.encode(fileInfo.getFileName(), "UTF-8"));
        } catch (UnsupportedEncodingException e2) {
            e2.printStackTrace();
        }
        byte[] buff = new byte[1024];
        BufferedInputStream bis = null;
        try {
            os = response.getOutputStream();
            String filePath = resPath + fileInfo.getFilePath().replace("/res/", "");
            java.io.File file = new java.io.File(filePath);
            if (!file.exists()) {
                log.error("下载失败，本地磁盘不存在,文件id {}",fileId);
                return "下载失败，本地磁盘不存在";

            }
            bis = new BufferedInputStream(new FileInputStream(file));
            int i = bis.read(buff);
            while (i != -1) {
                os.write(buff, 0, buff.length);
                os.flush();
                i = bis.read(buff);
            }
        } catch (ClientAbortException e1) { //中止连接不做处理
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return "下载成功";
    }



    /**
     * @param fileType : 文件后缀
     * @param fileName : 文件名，不包含后缀
     * @Description：下载 static/download目录下的文件
     */
    @GetMapping(value = "download/{fileType}/{fileName}")
//    ,produces = {"application/vnd.android.package-archive"}
    public String downloadFile(@PathVariable String fileType, @PathVariable String fileName, HttpServletRequest request, HttpServletResponse response) {
        if (StringUtils.isBlank(fileType)) return "下载失败，缺少参数【fileType】";
        if (StringUtils.isBlank(fileName)) return "下载失败，缺少参数【fileName】";
        String fileAllName = fileName + "." + fileType;
        //设置文件路径
        ClassPathResource classPathResource = new ClassPathResource("static/download/" + fileAllName);
        if (!classPathResource.exists()) return "下载失败，资源不存在";

        InputStream inputStream = null;
        String fileAllNameNew = fileAllName;
        long inputStreamLength = 0;

        String tmpFilePath = resPath + "tmp/downloadFile_" + System.currentTimeMillis();
        java.io.File tmpFile = new java.io.File(tmpFilePath);
        try {
            inputStream = classPathResource.getInputStream();
            fileAllNameNew = new String(fileAllName.getBytes("GBK"), "ISO-8859-1");

            FileUtils.copyToFile(inputStream, tmpFile);
            inputStreamLength = tmpFile.length();
        } catch (IOException e) {
            log.error("downloadFile.classPathResource.getInputStream()", e);
        }
        if (inputStream == null) {
            //删除临时文件
            FileUtils.deleteFile(tmpFilePath);
            return "文件下载失败，请稍候重试";
        }

        log.debug("downloadFile.fileAllNameNew={}", fileAllNameNew);
        log.debug("downloadFile.inputStreamLength={}", inputStreamLength);
        log.debug("downloadFile.classPathResource.getPath={}", classPathResource.getPath());
        //apk格式
        if ("apk".equalsIgnoreCase(fileType)) {
            response.setContentType("application/vnd.android.package-archive");
            response.setHeader("content-type", "application/vnd.android.package-archive");
        } else {
            response.setContentType("application/octet-stream");//
            response.setHeader("content-type", "application/octet-stream");
        }
        response.addHeader("Content-Length", "" + inputStreamLength);
        response.setHeader("Content-Disposition", "attachment;fileName=" + fileAllNameNew);// 设置文件名
        byte[] buffer = new byte[1024];
        BufferedInputStream bis = null;
        FileInputStream fis = null;
        try {

            fis = new FileInputStream(tmpFile);
            bis = new BufferedInputStream(fis);
            OutputStream os = response.getOutputStream();
            int i = bis.read(buffer);
            while (i != -1) {
                os.write(buffer, 0, i);
                i = bis.read(buffer);
            }

        } catch (Exception e) {
            log.error("downloadFile.readfile():{}", e);
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("downloadFile.inputStream.close", e);
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    log.error("downloadFile.fis.close", e);
                }
            }
            //删除临时文件
            FileUtils.deleteFile(tmpFilePath);
        }
        return "下载失败，请稍候重试";
    }

    /**
     * @Description：图片base64上传 【需要统一存放】
     */
    @RequestMapping(value = "/uploadImg", method = RequestMethod.POST)
    public RestResult<String> uploadImg(@RequestBody Map<String, String> reqData) {
        RestResult result = RestResult.ok("文件上传成功");
        //文件保存路径
        String fileSavePath = reqData.get("fileSavePath"), fileAccessPath;
        String refTable = reqData.get("fileRefTable");
        String refId = reqData.get("fileRefId");
        String fileRefIdOnlyOne = reqData.get("fileRefIdOnlyOne");
        String base64Str = reqData.get("base64Str");
        if (StringUtils.isBlank(fileSavePath)) fileSavePath = "tmp";
        fileSavePath += "/";
        fileAccessPath = fileSavePath;
        fileSavePath = resPath + fileSavePath;

        StringBuffer fileName = new StringBuffer();
        fileName.append(UUID.randomUUID().toString().replaceAll("-", ""));
        if (base64Str.indexOf("data:image/png;") != -1) {
            base64Str = base64Str.replace("data:image/png;base64,", "");
            fileName.append(".png");
        } else if (base64Str.indexOf("data:image/jpeg;") != -1) {
            base64Str = base64Str.replace("data:image/jpeg;base64,", "");
            fileName.append(".jpeg");
        } else {
            result.setMsg("文件上传失败,请选择.png.jpg格式的图片");
            result.setCode(ErrorCodes.COMMON_ERROR);
            return result;
        }

        java.io.File file = new java.io.File(fileSavePath, fileName.toString());
        byte[] fileBytes = Base64.getDecoder().decode(base64Str);
        try {
            FileUtils.mkdirs(fileSavePath);
            FileUtils.writeByteArrayToFile(file, fileBytes);
            //不为空的话，进行文件记录
            if (StringUtils.isNotBlank(refId)) {

                //同一个id只保留一个文件记录的时候，先删掉其他的
                if ("1".equals(fileRefIdOnlyOne)) {
                    QueryWrapper queryWrapper = Wrappers.query();
                    queryWrapper.eq("ref_id", refId);
                    service.remove(queryWrapper);
                }

                File fileSave = new File();
                fileSave.setRefId(refId);
                fileSave.setRefTable(refTable);
                fileSave.setFileName(fileName.toString());
                fileSave.setFilePath("/res/" + fileAccessPath + fileName);
                service.save(fileSave);
                result.setData(fileSave);
            }
        } catch (IOException e) {
            result.setMsg("文件上传失败," + e.getMessage());
            result.setCode(ErrorCodes.COMMON_ERROR);
        }
        return result;
    }

}
